﻿namespace DevilTools.Commons;

public class CommandLineParser {
    private readonly List<ValidationRule> _validationRules = [];
    private readonly List<string> _errors = [];

    /// <summary>
    /// 解析后的键值对参数（如 -f、--output）
    /// </summary>
    public Dictionary<string, string> Options { get; } = new(StringComparer.OrdinalIgnoreCase);

    private readonly List<string> _positionalArguments = [];

    /// <summary>
    /// 未标记的位置参数（如直接跟在命令后的文件路径）
    /// </summary>
    public IReadOnlyList<string> PositionalArguments => _positionalArguments.AsReadOnly();

    /// <summary>
    /// 验证错误列表
    /// </summary>
    public IReadOnlyList<string> Errors => _errors.AsReadOnly();

    /// <summary>
    /// 解析命令行参数
    /// </summary>
    public void Parse(string[] args) {
        for (var i = 0; i < args.Length; i++) {
            var arg = args[i];
            if (arg.StartsWith("--")) {
                // 长格式选项（--key=value 或 --key value）
                HandleLongOption(arg, ref i, args);
            } else if (arg.StartsWith("-")) {
                // 短格式选项（-k value 或 -k=value）
                HandleShortOption(arg, ref i, args);
            } else {
                // 位置参数（如文件路径）
                _positionalArguments.Add(arg);
            }
        }
    }

    /// <summary>
    /// 获取选项值，若不存在返回默认值
    /// </summary>
    public string GetOptionValue(string key, string defaultValue = null) {
        return Options.GetValueOrDefault(key, defaultValue);
    }

    /// <summary>
    /// 检查是否包含某个选项
    /// </summary>
    public bool HasOption(string key) => Options.ContainsKey(key);

    public bool GetBool(string key, bool defaultValue = false) {
        var value = GetOptionValue(key);
        if (string.IsNullOrWhiteSpace(value)) return defaultValue;
        return value.Equals("true", StringComparison.OrdinalIgnoreCase) ||
               value.Equals("1", StringComparison.OrdinalIgnoreCase);
    }

    public int GetInt32(string key, int defaultValue = 0) {
        var value = GetOptionValue(key);
        return string.IsNullOrWhiteSpace(value) ? defaultValue : int.Parse(value);
    }

    public long GetInt64(string key, long defaultValue = 0) {
        var value = GetOptionValue(key);
        return string.IsNullOrWhiteSpace(value) ? defaultValue : long.Parse(value);
    }

    public double GetDouble(string key, double defaultValue = 0) {
        var value = GetOptionValue(key);
        return string.IsNullOrWhiteSpace(value) ? defaultValue : double.Parse(value);
    }

    public float GetSingle(string key, float defaultValue = 0) {
        var value = GetOptionValue(key);
        return string.IsNullOrWhiteSpace(value) ? defaultValue : float.Parse(value);
    }

    #region 私有方法

    private void HandleLongOption(string arg, ref int index, string[] args) {
        var option = arg.Substring(2); // 移除前缀 --

        // 处理 --key=value 格式
        var equalsIndex = option.IndexOf('=');
        if (equalsIndex >= 0) {
            var key = option.Substring(0, equalsIndex);
            var value = option.Substring(equalsIndex + 1);
            Options[key] = UnescapeValue(value);
            return;
        }

        // 处理 --key value 格式
        if (index + 1 < args.Length && !IsOption(args[index + 1])) {
            Options[option] = UnescapeValue(args[++index]);
        } else {
            // 无值的选项（如 --verbose）
            Options[option] = "true";
        }
    }

    private void HandleShortOption(string arg, ref int index, string[] args) {
        var option = arg.Substring(1); // 移除前缀 -

        // 处理 -k=value 格式
        var equalsIndex = option.IndexOf('=');
        if (equalsIndex >= 0) {
            var key = option.Substring(0, equalsIndex);
            var value = option.Substring(equalsIndex + 1);
            Options[key] = UnescapeValue(value);
            return;
        }

        // 处理 -k value 格式
        if (index + 1 < args.Length && !IsOption(args[index + 1])) {
            Options[option] = UnescapeValue(args[++index]);
        } else {
            // 无值的选项（如 -v）
            Options[option] = "true";
        }
    }

    private static bool IsOption(string arg) => arg.StartsWith("-");

    private static string UnescapeValue(string value) {
        // 移除值两端的引号（如 "value" → value）
        if (value.Length >= 2 && (value.StartsWith("\"") && value.EndsWith("\"")) ||
            (value.StartsWith("'") && value.EndsWith("'"))) {
            return value.Substring(1, value.Length - 2);
        }

        return value;
    }

    #endregion

    #region 新增验证方法

    /// <summary>
    /// 标记选项为必填
    /// </summary>
    public CommandLineParser RequireOption(string optionName) {
        _validationRules.Add(new ValidationRule {
            Type = ValidationType.Required,
            OptionName = optionName
        });
        return this;
    }

    /// <summary>
    /// 验证选项为整数类型
    /// </summary>
    public CommandLineParser CheckIsInt(string optionName, int? min = null, int? max = null) {
        _validationRules.Add(new ValidationRule {
            Type = ValidationType.Integer,
            OptionName = optionName,
            Min = min,
            Max = max
        });
        return this;
    }

    /// <summary>
    /// 验证选项为布尔类型
    /// </summary>
    public CommandLineParser CheckIsBool(string optionName) {
        _validationRules.Add(new ValidationRule {
            Type = ValidationType.Boolean,
            OptionName = optionName
        });
        return this;
    }

    /// <summary>
    /// 添加自定义验证规则
    /// </summary>
    public CommandLineParser AddValidator(Action<CommandLineParser> validator) {
        _validationRules.Add(new ValidationRule {
            Type = ValidationType.Custom,
            CustomValidator = validator
        });
        return this;
    }

    /// <summary>
    /// 执行所有验证规则
    /// </summary>
    public void Validate() {
        foreach (var rule in _validationRules) {
            switch (rule.Type) {
                case ValidationType.Required:
                    if (!Options.ContainsKey(rule.OptionName))
                        _errors.Add($"必填参数 '{rule.OptionName}' 未提供");
                    break;

                case ValidationType.Integer:
                    if (Options.TryGetValue(rule.OptionName, out var intValue)) {
                        if (!int.TryParse(intValue, out int parsed)) {
                            _errors.Add($"参数 '{rule.OptionName}' 必须是整数");
                        } else {
                            if (rule.Min.HasValue && parsed < rule.Min.Value)
                                _errors.Add($"参数 '{rule.OptionName}' 不能小于 {rule.Min}");
                            if (rule.Max.HasValue && parsed > rule.Max.Value)
                                _errors.Add($"参数 '{rule.OptionName}' 不能大于 {rule.Max}");
                        }
                    }

                    break;

                case ValidationType.Boolean:
                    if (Options.TryGetValue(rule.OptionName, out var boolValue) &&
                        !bool.TryParse(boolValue, out _)) {
                        _errors.Add($"参数 '{rule.OptionName}' 必须是 true/false");
                    }

                    break;

                case ValidationType.Custom:
                    rule.CustomValidator?.Invoke(this);
                    break;
            }
        }

        if (_errors.Any()) {
            throw new ValidationException("参数验证失败", _errors);
        }
    }

    #endregion

    #region 内部类型

    private enum ValidationType {
        Required,
        Integer,
        Boolean,
        Custom
    }

    private class ValidationRule {
        public ValidationType Type { get; set; }
        public string OptionName { get; set; }
        public int? Min { get; set; }
        public int? Max { get; set; }
        public Action<CommandLineParser> CustomValidator { get; set; }
    }

    public class ValidationException : Exception {
        public List<string> Errors { get; }

        public ValidationException(string message, IEnumerable<string> errors)
            : base(message + "\n" + string.Join("\n", errors)) {
            Errors = errors.ToList();
        }
    }

    #endregion
}
